using DataTableMapperTests.Attributes;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace DataTableMapperTests.Mapping
{
    public static class PropertyMapHelper
    {

        public static void Map(DataRow row, PropertyInfo prop, object entity)
        {
            var dataAttr = AttributeHelper.GetDataNameAttr(prop);
            var columnNames = dataAttr.ValueNames;
            int index = dataAttr.Index;
            string customformat = dataAttr.CustomFormat;

            if (index != -1)
            {
                var propertyValue = row[index];
                if (propertyValue != DBNull.Value)
                {
                    ParsePrimitive(prop, entity, propertyValue, customformat);
                }
            }
            else
            {
                foreach (var columnName in columnNames)
                {
                    if (!string.IsNullOrWhiteSpace(columnName) && row.Table.Columns.Contains(columnName))
                    {
                        var propertyValue = row[columnName];
                        if (propertyValue != DBNull.Value)
                        {
                            ParsePrimitive(prop, entity, row[columnName], customformat);
                            break;
                        }
                    }
                }
            }
        }

        private static void ParsePrimitive(PropertyInfo prop, object entity, object value, string customformat)
        {
            if (prop.PropertyType == typeof(string))
            {
                prop.SetValue(entity, value.ToString().Trim(), null);
            }
            else if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?))
            {
                if (value == null)
                {
                    prop.SetValue(entity, null, null);
                }
                else
                {
                    prop.SetValue(entity, ParseBoolean(value.ToString()), null);
                }
            }
            else if (prop.PropertyType == typeof(long))
            {
                prop.SetValue(entity, long.Parse(value.ToString()), null);
            }
            else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?))
            {
                if (value == null)
                {
                    prop.SetValue(entity, null, null);
                }
                else
                {
                    prop.SetValue(entity, int.Parse(value.ToString()), null);
                }
            }
            else if (prop.PropertyType == typeof(decimal))
            {
                prop.SetValue(entity, decimal.Parse(value.ToString()), null);
            }
            else if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
            {
                double number;
                bool isValid = double.TryParse(value.ToString(), out number);
                if (isValid)
                {
                    prop.SetValue(entity, double.Parse(value.ToString()), null);
                }
            }
            else if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
            {
                var atts = prop.GetCustomAttributes<DataNamesAttribute>();

                DateTime date;
                bool isValid = DateTime.TryParse(value.ToString(), out date);
                if (isValid)
                {
                    prop.SetValue(entity, date, null);
                }
                else
                {
                    isValid = DateTime.TryParseExact(value.ToString(), customformat/*"MMddyyyy"*/, new CultureInfo("en-US"), DateTimeStyles.AssumeLocal, out date);
                    if (isValid)
                    {
                        prop.SetValue(entity, date, null);
                    }
                }
            }
            else if (prop.PropertyType == typeof(Guid))
            {
                Guid guid;
                bool isValid = Guid.TryParse(value.ToString(), out guid);
                if (isValid)
                {
                    prop.SetValue(entity, guid, null);
                }
                else
                {
                    isValid = Guid.TryParseExact(value.ToString(), customformat /*"B"*/, out guid);
                    if (isValid)
                    {
                        prop.SetValue(entity, guid, null);
                    }
                }
            }
        }

        public static bool ParseBoolean(object value)
        {
            if (value == null || value == DBNull.Value) return false;

            switch (value.ToString().ToLowerInvariant())
            {
                case "1":
                case "y":
                case "yes":
                case "true":
                    return true;

                case "0":
                case "n":
                case "no":
                case "false":
                default:
                    return false;
            }
        }
    }
}